package cn.myframe.shiro;


import cn.myframe.entity.sys.SysUserEntity;
import cn.myframe.redis.RedisService;
import cn.myframe.service.sys.SysMenuService;
import cn.myframe.service.sys.SysUserService;
import org.apache.commons.lang.StringUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;

import java.util.Set;
import java.util.concurrent.TimeUnit;

@Service
public class MyRealm extends AuthorizingRealm {

    @Autowired
    @Lazy
    private SysUserService sysUserService;
    @Autowired
    @Lazy
    private SysMenuService sysMenuService;
    @Autowired
    @Lazy
    private RedisService redisService;

    @Value("${server.timeout:30}")
    private int timeout;
   // private  final long Long_EXPIR_TIME = TimeUnit.HOURS.toMillis(10);

    /**
     * 必须重写此方法，不然Shiro会报错
     */
    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof JWTToken;
    }

    /**
     * 只有当需要检测用户权限的时候才会调用此方法，例如checkRole,checkPermission之类的
     * 授权(验证权限时调用)
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        SysUserEntity user = (SysUserEntity)principals.getPrimaryPrincipal();
        Long userId = user.getUserId();
        Set<String> permission = sysMenuService.getUserPermissions(userId);
        simpleAuthorizationInfo.addStringPermissions(permission);
        return simpleAuthorizationInfo;
    }

    /**
     * 默认使用此方法进行用户名正确与否验证，错误抛出异常即可。
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken auth)  {
        String token = (String) auth.getCredentials();
        String username = JWTUtil.getUsername(token);
        if (StringUtils.isEmpty(username)) {
            throw new AuthenticationException("token invalid");
        }
        SysUserEntity sysUserEntity = sysUserService.queryByName(username);
        if(sysUserEntity == null){
            throw new AuthenticationException("User didn't existed!");
        }
        switch (JWTUtil.verify(token, username, sysUserEntity.getPassword())) {
            case SUCCESS:
                return new SimpleAuthenticationInfo(sysUserEntity, token, "my_realm");
            case EXPIRED: //token过期查询freshToken是否存在，是否过期，未过期重新刷新accessToken
                String freshToken = (String)redisService.hmGet("JWTToken","JWT-SESSION-"+username);
                JWTUtil.VerifyCode code = JWTUtil.verify(freshToken, username, sysUserEntity.getPassword()+token);
                if( code == JWTUtil.VerifyCode.SUCCESS){
                    String accessToken = JWTUtil.sign(username, sysUserEntity.getPassword());
                    JWTToken jwtToken = (JWTToken)auth;
                    //更新freshToken
                    String newFreshToken = JWTUtil.sign(username, sysUserEntity.getPassword()+accessToken,TimeUnit.MINUTES.toMillis(timeout));
                    redisService.hmSet("JWTToken","JWT-SESSION-" + username, newFreshToken);
                    //把刷新的token设置到response头
                    jwtToken.buildResponse(accessToken);
                }else if(code ==  JWTUtil.VerifyCode.EXPIRED){
                    throw new AuthenticationException("EXPIRED ERROR");
                }else {
                    throw new AuthenticationException("TOKEN TIMEOUT");
                }
                break;
            default: throw new AuthenticationException("TOKEN TIMEOUT");
        }
        return new SimpleAuthenticationInfo(sysUserEntity, token, "my_realm");
    }
}
